home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / aztecnos.arc / PC100.C < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-13  |  12.3 KB  |  476 lines

  1. /* Interface driver for the PACCOMM PC-100 board for the IBM PC */
  2. /* UNFINISHED, DOESN'T WORK YET - work in progress by Bdale */
  3. /* currently only attempting to use the AMD7910 on Channel A */
  4.  
  5. #include <stdio.h>
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "iface.h"
  9. #include "pc100.h"
  10. #include "8530.h"
  11. #include "ax25.h"
  12. #include "trace.h"
  13.  
  14. extern struct iface *Ifaces;
  15. void pc0vec(),write_scc(),rts(),dopc();
  16. int pc_init(),pc_stop(),ax_send(),ax_output(),pc_raw();
  17. void (*getirq())();    /* Getirq is a function returning a pointer to
  18.                  * a function returning void */
  19.  
  20.  
  21. struct pc100 Pc100[NPC];
  22. void (*Pchandle[])() = { pc0vec };
  23. static struct hdlc Hdlc[2*NPC];
  24. int16 Npc;
  25.  
  26. /* Branch table for interrupt handler */
  27. void htxint(), hexint(), hrxint(), hspint();
  28. static void (*Svec[])() = {
  29.     htxint, hexint, hrxint, hspint
  30. };
  31.  
  32. /* Master interrupt handler for the PC-100 card. All interrupts come
  33.  * here first, then are switched out to the appropriate routine.
  34.  */
  35. void
  36. pcint(dev)
  37. int16 dev;
  38. {
  39.     register char iv;
  40.     register int16 pcbase;
  41.     struct hdlc *hp;
  42.  
  43.     Pc100[dev].ints++;
  44.     pcbase = Pc100[dev].addr;
  45.  
  46.     /* Read interrupt vector, including status, from channel B */
  47.     iv = read_scc(CTL+pcbase+CHANB,R2);
  48.  
  49.     hp = &Hdlc[2 * dev + ((iv & 0x80)? 0 : 1)];
  50.  
  51.     /* Now switch to appropriate routine */
  52.     (*Svec[(iv>>1) & 0x3])(hp);
  53.  
  54.     /* Reset interrupt pending state (register A only) */
  55.     write_scc(CTL+pcbase+CHANA,R0,RES_H_IUS);
  56.  
  57.     /* Wang the 8530 hardware interrupt acknowledge line - Bdale */
  58.     inportb(pcbase+INTACK);
  59. }
  60. /* HDLC Special Receive Condition interrupt
  61.  * The most common event that triggers this interrupt is the
  62.  * end of a frame; it can also be caused by a receiver overflow.
  63.  */
  64. static void
  65. hspint(hp)
  66. register struct hdlc *hp;
  67. {
  68.     register char c;
  69.  
  70.     hp->spints++;
  71.     c = read_scc(CTL+hp->base,R1);    /* Fetch latched bits */
  72.  
  73.     if((c & (END_FR|CRC_ERR)) == END_FR && hp->rcvbuf != NULLBUF
  74.         && hp->rcvbuf->cnt > 1){
  75.         /* End of valid frame */
  76.         hp->rcvbuf->cnt--;    /* Toss 1st crc byte */
  77.         enqueue(&hp->rcvq,hp->rcvbuf);
  78.         hp->rcvbuf = NULLBUF;
  79.         hp->rcvcnt++;
  80.     } else {
  81.         /* An overflow or CRC error occurred; restart receiver */
  82.         hp->crcerr++;
  83.         if(hp->rcvbuf != NULLBUF){
  84.             hp->rcp = hp->rcvbuf->data;
  85.             hp->rcvbuf->cnt = 0;
  86.         }
  87.     }
  88.     write_scc(CTL+hp->base,R0,ERR_RES);
  89. }
  90. /* HDLC SIO External/Status interrupts
  91.  * The only one of direct interest is a receiver abort; the other
  92.  * usual cause is a change in the modem control leads, so kick the
  93.  * transmit interrupt routine.
  94.  */
  95. static void
  96. hexint(hp)
  97. register struct hdlc *hp;
  98. {
  99.     hp->exints++;
  100.     hp->status = read_scc(CTL+hp->base,R0);    /* Fetch status */
  101.     if((hp->status & BRK_ABRT) && hp->rcvbuf != NULLBUF){
  102.         hp->aborts++;
  103.         /* Restart receiver */
  104.         hp->rcp = hp->rcvbuf->data;
  105.         hp->rcvbuf->cnt = 0;
  106.     }
  107.     write_scc(CTL+hp->base,R0,RES_EXT_INT);
  108.     write_scc(CTL+hp->base,R0,RES_H_IUS);
  109.     /* Kick the transmit interrupt routine for a possible modem change */
  110.     htxint(hp);
  111. }
  112. /* HDLC receiver interrupt handler. Allocates buffers off the freelist,
  113.  * fills them with receive data, and puts them on the receive queue.
  114.  */
  115. static void
  116. hrxint(hp)
  117. register struct hdlc *hp;
  118. {
  119.     register struct mbuf *bp;
  120.     register int16 base;
  121.  
  122.     hp->rxints++;
  123.     base = hp->base;
  124.     /* Allocate a receive buffer if not already present */
  125.     if((bp = hp->rcvbuf) == NULLBUF){
  126.         bp = hp->rcvbuf = alloc_mbuf(hp->bufsiz);
  127.         if(bp == NULLBUF){
  128.             /* No memory, abort receiver */
  129.             hp->nomem++;
  130.             write_scc(CTL+base,R3,ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
  131.             (void) inportb(base+DATA);
  132.             return;
  133.         }
  134.         hp->rcp = hp->rcvbuf->data;
  135.     }
  136.     while(read_scc(CTL+base,R0) & Rx_CH_AV){
  137.         if(bp->cnt++ >= hp->bufsiz){
  138.             /* Too large; abort the receiver, toss buffer */
  139.             hp->toobig++;
  140.             write_scc(CTL+base,R3,ENT_HM|RxENABLE|RxCRC_ENAB|Rx8);
  141.             (void) inportb(base+DATA);
  142.             free_p(bp);
  143.             hp->rcvbuf = NULLBUF;
  144.             break;
  145.         }
  146.         /* Normal save */
  147.         *hp->rcp++ = inportb(base+DATA);
  148.     }
  149. }
  150. int ctswait;
  151. /* HDLC transmit interrupt service routine
  152.  *
  153.  * The state variable tstate, along with some static pointers,
  154.  * represents the state of the transmit "process".
  155.  */
  156. static void
  157. htxint(hp)
  158. register struct hdlc *hp;
  159. {
  160.     register int16 base;
  161.     char i_state,c;
  162.  
  163.     i_state = dirps();
  164.     hp->txints++;
  165.     base = hp->base;
  166.     while(read_scc(CTL+base,R0) & Tx_BUF_EMP){
  167.         switch(hp->tstate){
  168.         /* First here for efficiency */
  169.         case ACTIVE:        /* Sending frame */
  170.             if(pullup(&hp->sndbuf,&c,1) == 1){
  171.                 outportb(base+DATA,c);
  172.             } else {
  173.                 /* Do this after sending the last byte */
  174.                 write_scc(CTL+base,R0,RES_Tx_P);
  175.                 if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF){
  176.                     switch(hp->mode){
  177.                     case CSMA:
  178.                         /* Begin transmitter shutdown */
  179.                         hp->tstate = FLUSH;
  180.                         break;
  181.                     case FULLDUP:
  182.                         hp->tstate = IDLE;
  183.                         break;
  184.                     }
  185.                 }
  186.             }
  187.             continue;
  188.         case IDLE:
  189.             /* Transmitter idle. Find a frame for transmission */
  190.             if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF)
  191.                 goto ret;
  192.  
  193.         case DEFER:    /* note fall-thru */
  194.             if(hp->mode == CSMA && (hp->status & DCD)){
  195.                 hp->tstate = DEFER;
  196.                 goto ret;
  197.             }
  198.             rts(base,ON);    /* Transmitter on */
  199.         case KEYUP:    /* note fall-thru */
  200.             if((hp->status & CTS) == 0){
  201.                 ctswait++;
  202.                 hp->tstate = KEYUP;
  203.                 goto ret;
  204.             }
  205.             write_scc(CTL+base,R0,RES_Tx_CRC);
  206.             pullup(&hp->sndbuf,&c,1);
  207.             outportb(hp->base+DATA,c);
  208.             hp->tstate = ACTIVE;
  209.             write_scc(CTL+base,R0,RES_EOM_L);
  210.             continue;
  211.         case FLUSH:    /* Sending flush character */
  212.             outportb(hp->base+DATA,(char)0);
  213.             hp->tstate = FIN2;
  214.             continue;
  215.         case FIN2:
  216.             write_scc(CTL+base,R0,SEND_ABORT);
  217.             hp->tstate = IDLE;
  218.             rts(base,OFF);
  219.             write_scc(CTL+base,R0,RES_Tx_P);
  220.             continue;
  221.         }
  222.     }
  223. ret:    restore(i_state);
  224. }
  225.  
  226. /* Set request-to-send on modem */
  227. static void
  228. rts(base,x)
  229. int16 base;
  230. int x;
  231. {
  232.     int16 cmd;
  233.  
  234.     if(x)
  235.         cmd = TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR;
  236.     else
  237.         cmd = TxCRC_ENAB | TxENAB | Tx8 | DTR;
  238.     write_scc(CTL+base,R5,cmd);
  239. }
  240. /* (re)Initialize HDLC controller parameters */
  241. static int
  242. hdlcparam(hp)
  243. register struct hdlc *hp;
  244. {
  245.     int16 tc;
  246.     char i_state;
  247.     register int16 base;
  248.  
  249.     /* Initialize 8530 channel for SDLC operation */
  250.     base = hp->base;
  251.     i_state = dirps();
  252.  
  253.     switch(base & 2){
  254.     case 0:
  255.         write_scc(CTL+base,R9,CHRA);    /* Reset channel A */
  256.         break;
  257.     case 2:
  258.         write_scc(CTL+base,R9,CHRB);    /* Reset channel B */
  259.         break;
  260.     }
  261.     /* Wait/DMA disable, Int on all Rx chars + spec condition,
  262.      * parity NOT spec condition, TxINT enable, Ext Int enable
  263.      */
  264.     write_scc(CTL+base,R1,INT_ALL_Rx | TxINT_ENAB | EXT_INT_ENAB);
  265.  
  266.     /* Dummy interrupt vector, will be modified by interrupt type
  267.      * (This probably isn't necessary)
  268.      */
  269.     write_scc(CTL+base,R2,0);
  270.  
  271.     /* 8 bit RX chars, auto enables off, no hunt mode, RxCRC enable,
  272.      * no address search, no inhibit sync chars, enable RX
  273.      */
  274.     write_scc(CTL+base,R3,Rx8|RxCRC_ENAB|RxENABLE);
  275.  
  276.     /* X1 clock, SDLC mode, Sync modes enable, parity disable
  277.      * (Note: the DPLL does a by-32 clock division, so it's not necessary
  278.      * to divide here).
  279.      */
  280.     write_scc(CTL+base,R4,X1CLK | SDLC | SYNC_ENAB);
  281.  
  282.     /* DTR On, 8 bit TX chars, no break, TX enable, SDLC CRC,
  283.      * RTS off, TxCRC enable
  284.      */
  285.     write_scc(CTL+base,R5,DTR|Tx8|TxENAB|TxCRC_ENAB);
  286.  
  287.     /* SDLC flag */
  288.     write_scc(CTL+base,R7,FLAG);
  289.  
  290.     /* No reset, status low, master int enable, enable lower chain,
  291.      * no vector, vector includes status
  292.      */
  293.     write_scc(CTL+base,R9,MIE|NV|VIS);
  294.     /* CRC preset 1, NRZI encoding, no active on poll, flag idle,
  295.      * flag on underrun, no loop mode, 8 bit sync
  296.      */
  297.     write_scc(CTL+base,R10,CRCPS|NRZI);
  298.  
  299.     /* Board no longer channel-specific for clk.  The board should be set
  300.      * up to run from the 4.9152Mhz onboard crystal connected to PCLK.
  301.      * Both channels get receive clock at 32x from PCLK via the DPLL,
  302.      * with TRxC as an output, via a 4040 div by 32 counter to RTxC set
  303.      * us as an input to provide the transmit clock.
  304.      */
  305.  
  306.     /*            TRxC = BR Generator Output, TRxC O/I,
  307.      *          transmit clock = RTxC pin, 
  308.      *          receive clock = DPLL output
  309.      */
  310.     write_scc(CTL+base,R11,TRxCBR|TRxCOI|TCRTxCP|RCDPLL);
  311.  
  312.     /* Compute and load baud rate generator time constant
  313.      * DPLL needs x32 clock
  314.      * XTAL is defined in pc100.h to be the crystal clock / (2 * 32)
  315.      */
  316.     tc = XTAL/(hp->speed) - 2;
  317.     write_scc(CTL+base,R12,tc & 0xff);
  318.     write_scc(CTL+base,R13,(tc >> 8) & 0xff);
  319.  
  320.     write_scc(CTL+base,R14,SNRZI);    /* Set NRZI mode */
  321.     write_scc(CTL+base,R14,SSBR);    /* Set DPLL source = BR generator */
  322.     write_scc(CTL+base,R14,SEARCH);    /* Enter search mode */
  323.     /* Set baud rate gen source = PCLK, enable baud rate gen */
  324.     write_scc(CTL+base,R14,BRENABL|BRSRC);
  325.  
  326.     /* Break/abort IE, TX EOM IE, CTS IE, no SYNC/HUNT IE, DCD IE,
  327.      * no Zero Count IE
  328.      */
  329.     write_scc(CTL+base,R15,BRKIE|TxUIE|CTSIE|DCDIE);
  330.  
  331.     restore(i_state);
  332.     if(hp->mode == FULLDUP){
  333.         rts(base,ON);
  334.     } else if(hp->tstate == IDLE){
  335.         rts(base,OFF);
  336.     }
  337.     return 0;
  338. }
  339. /* Attach a PC-100 interface to the system
  340.  * argv[0]: hardware type, must be "pc100"
  341.  * argv[1]: I/O address, e.g., "0x380"
  342.  * argv[2]: vector, e.g., "2"
  343.  * argv[3]: mode, must be:
  344.  *        "ax25" (AX.25 UI frame format)
  345.  * argv[4]: interface label, e.g., "pc0"
  346.  * argv[5]: receiver packet buffer size in bytes
  347.  * argv[6]: maximum transmission unit, bytes
  348.  * argv[7]: interface speed, e.g, "9600"
  349.  */
  350. int
  351. pc_attach(argc,argv)
  352. int argc;
  353. char *argv[];
  354. {
  355.     register struct iface *if_pca,*if_pcb;
  356.     struct hdlc *hp;
  357.     int dev;
  358.  
  359.     if(Npc >= NPC){
  360.         printf("Too many pc100 controllers\n");
  361.         return -1;
  362.     }
  363.     if(if_lookup(argv[4]) != NULLIF){
  364.         printf("Interface %s already exists\n",argv[4]);
  365.         return -1;
  366.     }
  367.     dev = Npc++;
  368.  
  369.     /* Initialize hardware-level control structure */
  370.     Pc100[dev].addr = htoi(argv[1]);
  371.     Pc100[dev].vec = htoi(argv[2]);
  372.     /* Initialize modems */
  373.     outportb(Pc100[dev].addr + MODEM_CTL,(char)0x22);
  374.  
  375.     /* Save original interrupt vector */
  376.     Pc100[dev].oldvec = getirq(Pc100[dev].vec);
  377.     /* Set new interrupt vector */
  378.     if(setirq(Pc100[dev].vec,Pchandle[dev]) == -1){
  379.         printf("IRQ %u out of range\n",Pc100[dev].vec);
  380.         Npc--;
  381.         return -1;
  382.     }
  383.     /* Create interface structures and fill in details */
  384.     if_pca = (struct iface *)calloc(1,sizeof(struct iface));
  385.     if_pcb = (struct iface *)calloc(1,sizeof(struct iface));
  386.  
  387.     if_pca->name = strdup(argv[4]);
  388.     if_pcb->name = strdup(argv[4]);
  389.     if_pcb->name[strlen(argv[4]) - 1]++;    /* kludge */
  390.     if_pcb->mtu = if_pca->mtu = atoi(argv[6]);
  391.     if_pca->dev = 2*dev;
  392.     if_pcb->dev = 2*dev + 1;
  393.     if_pcb->stop = if_pca->stop = pc_stop;
  394.     if_pcb->output = if_pca->output = ax_output;
  395.     if_pcb->raw = pc_raw;
  396.  
  397.     if(strcmp(argv[3],"ax25") == 0){
  398.  
  399.         axarp();
  400.         if(Mycall.call[0] == '\0'){
  401.             printf("set mycall first\n");
  402.             free((char *)if_pca);
  403.             free((char *)if_pcb);
  404.             return -1;
  405.         }        
  406.         if_pcb->send = if_pca->send = ax_send;
  407.         if(if_pcb->hwaddr == NULLCHAR)
  408.             if_pcb->hwaddr = malloc(sizeof(Mycall));
  409.         memcpy(if_pcb->hwaddr,(char *)&Mycall,sizeof(Mycall));
  410.     } else {
  411.         printf("Mode %s unknown for interface %s\n",
  412.             argv[3],argv[4]);
  413.         free((char *)if_pca);
  414.         free((char *)if_pcb);
  415.         return -1;
  416.     }
  417.     if_pca->next = if_pcb;
  418.     if_pcb->next = Ifaces;
  419.     Ifaces = if_pca;
  420.  
  421.     hp = &Hdlc[2*dev+1];
  422.     hp->speed = (int16)atoi(argv[7]);
  423.     hp->base = Pc100[dev].addr + CHANB;
  424.     hp->bufsiz = atoi(argv[5]);
  425.     hdlcparam(hp);
  426.  
  427.     hp = &Hdlc[2*dev];
  428.     hp->speed = (int16)atoi(argv[7]);
  429.     hp->base = Pc100[dev].addr + CHANA;
  430.     hp->bufsiz = atoi(argv[5]);
  431.     hdlcparam(hp);
  432.  
  433.     /* Clear mask (enable interrupt) in 8259 interrupt controller */
  434.     clrbit(INTMASK,(char)(1<<Pc100[dev].vec));
  435.  
  436.     return 0;
  437. }
  438. int
  439. pc_stop(iface)
  440. struct iface *iface;
  441. {
  442.     int16 dev;
  443.  
  444.     dev = iface->dev;
  445.     if(dev & 1)
  446.         return 0;
  447.     dev >>= 1;    /* Convert back into PC100 number */
  448.     /* Turn off interrupts */
  449.     maskoff(Pc100[dev].vec);
  450.  
  451.     /* Restore original interrupt vector */
  452.     setirq(Pc100[dev].vec,Pc100[dev].oldvec);
  453.  
  454.     /* Force hardware reset */
  455.     write_scc(CTL+Pc100[dev].addr + CHANA,R9,FHWRES);
  456.     return 0;
  457. }
  458.     
  459. /* Send raw packet on PC-100 */
  460. int
  461. pc_raw(iface,bp)
  462. struct iface *iface;
  463. struct mbuf *bp;
  464. {
  465.     char kickflag;
  466.     struct hdlc *hp;
  467.  
  468.     dump(iface,IF_TRACE_OUT,TYPE_AX25,bp);
  469.     hp = &Hdlc[iface->dev];
  470.     kickflag = (hp->sndq == NULL);
  471.     enqueue(&hp->sndq,bp);
  472.     if(kickflag)
  473.         htxint(&Hdlc[iface->dev]);
  474.     return 0;
  475. }
  476.